/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @file
 * RequestManager.h
 *
 * Manages external or internal event requests activating or deactivating
 * appropriate event handlers.
 */

#ifndef _REQUEST_MANAGER_H_
#define _REQUEST_MANAGER_H_

#include "Util.h"

#include "AgentBase.h"
#include "AgentMonitor.h"
#include "AgentEventRequest.h"
#include "PacketParser.h"

namespace jdwp {

    /** Type for list of event requests. */
    typedef JDWPVector<AgentEventRequest> RequestList;

    /** Type for list iterator for event requests. */
    typedef RequestList::iterator RequestListIterator;

    /** Class for storing and handling info about combined events
      * with the same location in the same thread.
      */
    class CombinedEventsInfo : public AgentBase {
    public:
        enum CombinedEventsKind {
            COMBINED_EVENT_METHOD_ENTRY,
            COMBINED_EVENT_SINGLE_STEP,
            COMBINED_EVENT_BREAKPOINT,
            COMBINED_EVENT_METHOD_EXIT,
            COMBINED_EVENT_COUNT // number of event kinds
        };

        struct CombinedEventsList {
            // stored requests to generate events
            RequestID *list;
            // number of stored requests
            jint count;
            // number of event callbacks to ignore (0 or 1)
            jint ignored;
        };

        // list af all combined events
        CombinedEventsList m_combinedEventsLists[COMBINED_EVENT_COUNT];
        // event info for initial event
        EventInfo m_eInfo;

    public:

        /**
         * A constructor.
         */
        CombinedEventsInfo();

        /**
         * A destructor.
         */
        ~CombinedEventsInfo();
        
        /**
         * Stores information about location of initial combined event.
         */
        int Init(JNIEnv *jni, EventInfo &eInfo);

        /**
         * Clears information about stored events.
         */
        void Clean(JNIEnv *jni);

        /**
         * Returns number of all combined events.
         */
        int GetEventsCount() const;

        /**
         * Returns number of all ignored callbacks.
         */
        int GetIgnoredCallbacksCount() const;

        /**
         * Counts occured event callback and decreaces number of ignored callbacks
         * for this kind of combined event.
         */
        void CountOccuredCallback(CombinedEventsKind combinedKind);

    }; //CombinedEventsInfo

    /** Type for list of stored combined events info in all threads. */
    typedef JDWPVector<CombinedEventsInfo> CombinedEventsInfoList;

    /**
     * The class manages events generated by the target VM and passes them to
     * <code>EventDispatcher</code>.
     */
    class RequestManager : public AgentBase {

    public:

        /**
         * A constructor.
         */
        RequestManager();

        /**
         * A destructor.
         */
        ~RequestManager();

        /**
         * Initializes the instance of <code>RequestManager</code>.
         *
         * @param jni - the JNI interface pointer
         *
         * @throws AgentException.
         */
        void Init(JNIEnv* jni);

        /**
         * Cleanups the instance of <code>RequestManager</code>.
         *
         * @param jni - the JNI interface pointer
         *
         * @throws AgentException.
         */
        void Clean(JNIEnv* jni);

        /**
         * Resets the instance of <code>RequestManager</code>.
         *
         * @param jni - the JNI interface pointer
         *
         * @throws AgentException.
         */
        void Reset(JNIEnv* jni);

        /**
         * Adds the given internal request to the list of requests of corresponding types.
         *
         * @param jni     - the JNI interface pointer
         * @param request - the <code>AgentEventRequest</code> instance pointer
         *
         * @throws AgentException.
         */
        int AddInternalRequest(JNIEnv* jni, AgentEventRequest* request);

        /**
         * Adds the given request to the list of requests of corresponding types 
         * and assigns the unique ID for the request.
         *
         * @param jni     - the JNI interface pointer
         * @param request - the AgentEventRequest instance pointer
         *
         * @throws AgentException.
         */
        RequestID AddRequest(JNIEnv* jni, AgentEventRequest* request);

        /**
         * Removes a request of the given kind with the given request ID.
         *
         * @param jni   - the JNI interface pointer
         * @param kind  - the JDWP event-request kind
         * @param id    - the request ID to delete
         *
         * @throws AgentException.
         */
        int DeleteRequest(JNIEnv* jni, jdwpEventKind kind, RequestID id);

        /**
         * Removes the given request from the corresponding request list.
         *
         * @param jni     - the JNI interface pointer
         * @param request - the pointer to the request to delete
         *
         * @throws AgentException.
         */
        int DeleteRequest(JNIEnv* jni, AgentEventRequest* request);

        /**
         * Removes all requests with the given kind from the corresponding request list.
         *
         * @param jni       - the JNI interface pointer
         * @param eventKind - the JDWP event-request kind
         *
         * @throws AgentException.
         */
        void DeleteAllRequests(JNIEnv* jni, jdwpEventKind eventKind);

        /**
         * Removes all requests with <code>JDWP_EVENT_BREAKPOINT</code> 
         * kind from the corresponding request list.
         *
         * @param jni - the JNI interface pointer
         *
         * @throws AgentException.
         */
        void DeleteAllBreakpoints(JNIEnv* jni);

        /**
         * Returns the name of the given JDWP event kind.
         *
         * @param kind - the JDWP event kind
         */
        const char* GetEventKindName(jdwpEventKind kind) const;

        /**
         * Enables an internal step request for the <code>PopFrame</code> command.
         *
         * @param jni    - the JNI interface pointer
         * @param thread - the <code>PopFrames</code> command-performed thread 
         *
         * @exception <code>AgentException</code> is thrown, if any error occurs.
         */
        int EnableInternalStepRequest(JNIEnv* jni, jthread thread);

        /**
         * Disables an internal step request after the <code>PopFrame</code> command.
         *
         * @param jni    - the JNI interface pointer
         * @param thread - the <code>PopFrames</code> command-performed thread 
         *
         * @exception <code>AgentException</code> is thrown, if any error occurs.
         */
        int DisableInternalStepRequest(JNIEnv* jni, jthread thread);

		 /**
         * Returns the monitor for exception handling.
         *
         */
		AgentMonitor* GetExceptionMonitor();

        // event callbacks

        /**
         * <code>ClassPrepare</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         * @param cls    - the prepared Java class
         */
        static void JNICALL HandleClassPrepare(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jclass cls);

        /**
         * <code>ClassUnload</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         * @param cls    - the unloaded Java class
         */
        static void JNICALL HandleClassUnload(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jclass cls);

        /**
         * <code>ThreadEnd</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         */
        static void JNICALL HandleThreadEnd(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread);

        /**
         * <code>ThreadStart</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         */
        static void JNICALL HandleThreadStart(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread);

        /**
         * <code>VMInit</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         */
        static void JNICALL HandleVMInit(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread);

        /**
         * <code>VMDeath</code> event callbacks.
         *
         * @param jvmti - the JVMTI interface pointer
         * @param jni   - the JNI interface pointer
         */
        static void JNICALL HandleVMDeath(jvmtiEnv *jvmti, JNIEnv *jni);

        /**
         * Breakpoint event callbacks.
         *
         * @param jvmti    - the JVMTI interface pointer
         * @param jni      - the JNI interface pointer
         * @param thread   - the Java thread-generating event
         * @param method   - the method ID where breakpoint had occurred
         * @param location - the location where breakpoint had occurred
         */
        static void JNICALL HandleBreakpoint(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jlocation location);

        /**
         * Exception event callbacks.
         *
         * @param jvmti          - the JVMTI interface pointer
         * @param jni            - the JNI interface pointer
         * @param thread         - the Java thread-generating event
         * @param method         - the method ID where exception had occurred
         * @param location       - the location where exception had occurred
         * @param exception      - the Java exception
         * @param catch_method   - the method ID where exception was caught
         * @param catch_location - the location where exception was caught
         */
        static void JNICALL HandleException(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jlocation location,
            jobject exception, jmethodID catch_method,
            jlocation catch_location);

        /**
         * <code>MethodEntry</code> event callbacks.
         *
         * @param jvmti  - the JVMTI interface pointer
         * @param jni    - the JNI interface pointer
         * @param thread - the Java thread-generating event
         * @param method - the entering method ID
         */
        static void JNICALL HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method);

        /**
         * <code>MethodExit</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   - the Java thread-generating event
         * @param method                   - the existing method ID
         * @param was_popped_by_exception  - whether the exception-popped frame or 
         *                                   normal return occurred
         * @param return_value             - the return value
         */
        static void JNICALL HandleMethodExit(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jboolean was_popped_by_exception,
            jvalue return_value);

        /**
         * <code>FieldAccess</code> event callbacks.
         *
         * @param jvmti        - the JVMTI interface pointer
         * @param jni          - the JNI interface pointer
         * @param thread       - the Java thread-generating event
         * @param method       - the accessed-field method  
         * @param location     - the <code>FieldAccess</code> event location 
         * @param field_class  - the accessed-field class 
         * @param object       - the accessed-field owner object
         * @param field        - the accessed-field ID 
         */
        static void JNICALL HandleFieldAccess(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jlocation location,
            jclass field_class, jobject object, jfieldID field);

        /**
         * <code>FieldModification</code> event callbacks.
         *
         * @param jvmti        - the JVMTI interface pointer
         * @param jni          - the JNI interface pointer
         * @param thread       - the Java thread-generating event
         * @param method       - the modified-field method 
         * @param location     - the <code>FieldModification</code> event location 
         * @param field_class  - the modified-field class 
         * @param object       - the modified-field owner object
         * @param field        - the accessed-field ID 
         * @param sig          - the reference-type signature of a new field value
         * @param value        - a new value
         */
        static void JNICALL HandleFieldModification(jvmtiEnv* jvmti,
            JNIEnv* jni, jthread thread, jmethodID method, jlocation location,
            jclass field_class, jobject object, jfieldID field,
            char sig, jvalue value);

        /**
         * <code>SingleStep</code> event callbacks.
         *
         * @param jvmti     - the JVMTI interface pointer
         * @param jni       - the JNI interface pointer
         * @param thread    - the Java thread-generating event
         * @param method    - the single-step occurred method 
         * @param location  - the single-step occurred location
         */
        static void JNICALL HandleSingleStep(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jlocation location);

        /**
         * <code>FramePop</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   - the Java thread-generating event
         * @param method                   - the ID of popped method
         * @param was_popped_by_exception  - whether the exception-popped frame
         *                                   or normal return occurred
         */
        static void JNICALL HandleFramePop(jvmtiEnv* jvmti, JNIEnv* jni,
            jthread thread, jmethodID method, jboolean was_popped_by_exception);

        // New event callbacks for Java 6
        /**
         * <code>MonitorContendedEnter</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   - the thread attempting to enter the monitor 
         * @param object                   - the monitor object
         */
        static void JNICALL HandleMonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv* jni,
            jthread thread, jobject object);
        
        /**
         * <code>MonitorContendedEntered</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   -  the thread entering the monitor 
         * @param object                   - the monitor object
         */
        static void JNICALL HandleMonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv* jni,
            jthread thread, jobject object);

        /**
         * <code>MonitorWait</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   - the  the thread that was finished waiting 
         * @param object                   - the monitor object
         * @param timeout                  - the number of milliseconds the thread will wait 
         */
        static void JNICALL HandleMonitorWait(jvmtiEnv *jvmti, JNIEnv* jni, 
            jthread thread, jobject object, jlong timeout);

        /**
         * <code>MonitorWaited</code> event callbacks.
         *
         * @param jvmti                    - the JVMTI interface pointer
         * @param jni                      - the JNI interface pointer
         * @param thread                   - the Java thread-generating event
         * @param object                   - the monitor object
         * @param timeout               - true if the monitor timed out 
         */
        static void JNICALL HandleMonitorWaited(jvmtiEnv *jvmti, JNIEnv* jni, 
            jthread thread, jobject object, jboolean timed_out);


    private:

        /**
         * Get the return type of the method
         */
        static jdwpTag MethodReturnType(jvmtiEnv *env, jmethodID method);
        
        /**
         * Generate event whose event kind is MethodExitWithReturnValue 
         */
        static void JNICALL HandleMethodExitWithReturnValue(jvmtiEnv* jvmti, JNIEnv* jni,
        jthread thread, jmethodID method, jboolean was_popped_by_exception,
        jvalue return_value);

        /**
         * Generate event whose event kind is MethodExit
         */
        static void JNICALL HandleMethodExitWithoutReturnValue(jvmtiEnv* jvmti, JNIEnv* jni,
        jthread thread, jmethodID method, jboolean was_popped_by_exception,
        jvalue return_value);

        /**
         * Enables/disables all appropriate events for given Breakpoint event request. 
         */
        int ControlBreakpoint(JNIEnv* jni, AgentEventRequest* request,
            bool enable);

        /**
         * Enables/disables all appropriate events for given Watchpoint event request. 
         */
        int ControlWatchpoint(JNIEnv* jni, AgentEventRequest* request,
            bool enable);

        /**
         * Enables/disables all appropriate events for given ClassUnload event request.
         *
         * @return index of corresponding JVMTI extension event or 0 if not supported
         */
        jint ControlClassUnload(JNIEnv* jni, AgentEventRequest* request, 
            bool enable);

        /**
         * Enables/disables all appropriate events for given event request. 
         */
        int ControlEvent(JNIEnv* jni, AgentEventRequest* request, bool enable);

        /**
         * Returns all registered event requests for given event kind. 
         */
        RequestList& GetRequestList(jdwpEventKind kind);

        /**
         * Deletes step request for given thread if any. 
         */
        void DeleteStepRequest(JNIEnv* jni, jthread thread);

        /**
         * Finds step request for given thread if any. 
         */
        StepRequest* FindStepRequest(JNIEnv* jni, jthread thread);

        /**
         * Write data for all combined events to event packet. 
         */
        EventComposer* CombineEvents(JNIEnv* jni, CombinedEventsInfo* combEventsInfo, jdwpSuspendPolicy sp);

        /**
         * Find existing info about combined events for given thread. 
         */
        CombinedEventsInfoList::iterator FindCombinedEventsInfo(JNIEnv *jni, jthread thread);

        /**
         * Strore new info about combined events.
         */
        void AddCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfo* info);

        /**
         * Remove given info about combined events. 
         */
        void DeleteCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfoList::iterator p);

        /**
         * Checks if this combined event was predicted and should be ignored.
         * It also removes combined events info after all predicted events occured.
         */
        bool IsPredictedCombinedEvent(JNIEnv *jni, EventInfo& eInfo, 
                    CombinedEventsInfo::CombinedEventsKind combinedKind);

        /**
         * Cleans all stored info about combined events.
         */
        void DeleteAllCombinedEventsInfo(JNIEnv *jni);

        /**
         * Checks if given location is first location of a method.
         */
        //bool IsMethodEntryLocation(JNIEnv* jni, EventInfo& eInfo);

        /**
         * Checks if given location is last location of a method.
         */
        bool IsMethodExitLocation(JNIEnv* jni, EventInfo& eInfo);

        /**
         * Creates list of event request IDs matched to fired event.
         */
        void GenerateEvents(JNIEnv* jni, EventInfo &event, jint &eventCount, 
                    RequestID* &eventList, jdwpSuspendPolicy &sp);

        RequestID m_requestIdCount;
        AgentMonitor* m_requestMonitor;
        AgentMonitor* m_combinedEventsMonitor;
		AgentMonitor* m_exceptionMonitor;

        RequestList m_singleStepRequests;
        RequestList m_breakpointRequests;
        RequestList m_framePopRequests;
        RequestList m_exceptionRequests;
        RequestList m_userDefinedRequests;
        RequestList m_threadStartRequests;
        RequestList m_threadEndRequests;
        RequestList m_classPrepareRequests;
        RequestList m_classUnloadRequests;
        RequestList m_classLoadRequests;
        RequestList m_fieldAccessRequests;
        RequestList m_fieldModificationRequests;
        RequestList m_exceptionCatchRequests;
        RequestList m_methodEntryRequests;
        RequestList m_methodExitRequests;
        RequestList m_vmDeathRequests;
		RequestList m_vmStartRequests;
        // New list of event request IDs for Java 6
        RequestList m_methodExitWithReturnValueRequests;
        RequestList m_monitorContendedEnterRequests;
        RequestList m_monitorContendedEnteredRequests;
        RequestList m_monitorWaitRequests;
        RequestList m_monitorWaitedRequests;


        CombinedEventsInfoList m_combinedEventsInfoList;
    };

}

#endif // _REQUEST_MANAGER_H_
